﻿using System.Collections.ObjectModel;
using System;
using System.Xml.Linq;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.IO;
using System.Net.Http;
using System.IO.Compression;
using Windows.Storage;
using System.Globalization;

using SeriesManager.Enum;
using SeriesManager.Control;
using SeriesManager.Model.Comparer;
using SeriesManager.Extension;
using SeriesManager.Model.Banner;

namespace SeriesManager.Model
{
    public sealed class SeriesModel : BindableBase
    {
        #region Properties

        private readonly int id;
        private DayOfWeek airDay;
        private TimeSpan airTime;
        private DateTime firstAired;
        private string[] genres;
        private string imdbID;
        private Languages language;
        private string network;
        private string overview;
        private double rating = 0.1;
        private int ratingCount;
        private int runtime;
        private string name;
        private bool active;
        private FanArtBannerModel fanart;
        private bool subscription;
        private WatchState state = WatchState.Unwatched;

        public SeasonModel this[int seasonNumber]
        {
            get
            {
                return this.Seasons.FirstOrDefault(S => S.Number == seasonNumber);
            }
        }

        public ObservableSortedList<SeasonBannerModel> SeasonBanners
        {
            get;
            private set;
        }

        public ObservableSortedList<WideBannerModel> WideBanners
        {
            get;
            private set;
        }

        public ObservableSortedList<FanArtBannerModel> FanArtBanners
        {
            get;
            private set;
        }

        public ObservableSortedList<PosterBannerModel> PosterBanners
        {
            get;
            private set;
        }

        public ObservableSortedList<SeasonModel> Seasons
        {
            get;
            private set;
        }

        public ObservableSortedList<EpisodeModel> Watched
        {
            get;
            private set;
        }

        public ObservableSortedList<EpisodeModel> Unwatched
        {
            get;
            private set;
        }

        public ObservableSortedList<EpisodeModel> UnAired
        {
            get;
            private set;
        }

        public ObservableCollection<ActorModel> Actors
        {
            get;
            private set;
        }

        public int ID
        {
            get
            {
                return this.id;
            }
        }

        public DayOfWeek AirDay
        {
            get
            {
                return this.airDay;
            }
            set
            {
                base.SetProperty(ref this.airDay, value);
            }
        }

        public string ImdbID
        {
            get 
            { 
                return this.imdbID; 
            }
            set 
            {
                base.SetProperty(ref this.imdbID, value); 
            }
        }

        public Languages Language
        {
            get 
            { 
                return this.language; 
            }
            set 
            {
                base.SetProperty(ref this.language, value);
            }
        }

        public string Name
        {
            get 
            { 
                return this.name; 
            }
            set 
            { 
                base.SetProperty(ref this.name, value); 
            }
        }

        public double Rating
        {
            get 
            { 
                return this.rating; 
            }
            set 
            { 
                base.SetProperty(ref this.rating, value); 
            }
        }

        public int RatingCount
        {
            get
            {
                return this.ratingCount;
            }
            set
            {
                base.SetProperty(ref this.ratingCount, value);
            }
        }

        public TimeSpan AirTime
        {
            get 
            { 
                return this.airTime; 
            }
            set 
            { 
                base.SetProperty(ref this.airTime, value); 
            }
        }

        public DateTime FirstAired
        {
            get 
            { 
                return this.firstAired; 
            }
            set 
            { 
                base.SetProperty(ref this.firstAired, value); 
            }
        }

        public int Runtime
        {
            get 
            { 
                return this.runtime; 
            }
            set 
            { 
                base.SetProperty(ref this.runtime, value); 
            }
        }

        public string[] Genre
        {
            get 
            { 
                return this.genres; 
            }
            set 
            { 
                base.SetProperty(ref this.genres, value); 
            }
        }

        public string Network
        {
            get 
            {
                return this.network; 
            }
            set 
            { 
                base.SetProperty(ref this.network, value); 
            }
        }

        public string Overview
        {
            get 
            { 
                return this.overview; 
            }
            set 
            { 
                base.SetProperty(ref this.overview, value); 
            }
        }

        public bool Active
        {
            get 
            { 
                return this.active; 
            }
            set 
            { 
                base.SetProperty(ref this.active, value); 
            }
        }

        public FanArtBannerModel FanArt
        {
            get 
            { 
                return this.fanart; 
            }
            set 
            {
                base.SetProperty(ref this.fanart, value);
            }
        }

        public PosterBannerModel Poster
        {
            get
            {
                return this.PosterBanners.FirstOrDefault();
            }
        }

        public WideBannerModel Banner
        {
            get
            {
                return this.WideBanners.FirstOrDefault();
            }
        }

        public bool Subscription
        {
            get
            {
                return this.subscription;
            }
            set
            {
                base.SetProperty(ref this.subscription, value);
            }
        }

        public WatchState State
        {
            get
            {
                return this.state;
            }
            set
            {
                if (value != WatchState.UnAired)
                {
                    foreach (var season in this.Seasons)
                    {
                        season.State = value;
                    }
                }
            }
        }

        public int Count
        {
            get
            {
                return this.Seasons.Sum(S => S.Episodes.Count);
            }
        }

        public IEnumerable<EpisodeModel> AirToday
        {
            get
            {
                return this.UnAired.Where(E => E.FirstAired.Date.CompareTo(DateTime.Now.Date) == 0);
            }
        }

        public IEnumerable<EpisodeModel> AirWeek
        {
            get
            {
                var today = DateTime.Today;
                var startOfNextWeek = today.StartOfWeek(DayOfWeek.Monday).AddDays(7);

                return this.UnAired.Where(E => E.FirstAired.Date.CompareTo(today.Date) > 0 && E.FirstAired.Date.CompareTo(startOfNextWeek.Date) < 0);
            }
        }

        public IEnumerable<EpisodeModel> AirMonth
        {
            get
            {
                DateTime today = DateTime.Today;
                DateTime nextMonth = today.AddMonths(1);
                var startOfNextWeek = today.StartOfWeek(DayOfWeek.Monday).AddDays(7);
                nextMonth = new DateTime(nextMonth.Year, nextMonth.Month, 1);

                return this.UnAired.Where(E => E.FirstAired.Date.CompareTo(startOfNextWeek.Date) >= 0 && E.FirstAired.Date.CompareTo(nextMonth.Date) < 0);
            }
        }

        public IEnumerable<EpisodeModel> AirYear
        {
            get
            {
                var today = DateTime.Today;
                return this.UnAired.Where(E => E.FirstAired.Month > today.Month && E.FirstAired.Year == today.Year);
            }
        }

        public IEnumerable<EpisodeModel> AirOther
        {
            get
            {
                var nextYear = new DateTime(DateTime.Today.Year + 1, 1, 1);
                return this.UnAired.Where(E => E.FirstAired.Date.CompareTo(nextYear.Date) >= 0 || E.FirstAired.Date.CompareTo(DateTime.MinValue) == 0);
            }
        }

        #endregion

        #region Constructor

        public SeriesModel(int seriesID)
        {
            this.id = seriesID;

            this.SeasonBanners = new ObservableSortedList<SeasonBannerModel>(1000, new BannerBaseComparer());
            this.WideBanners = new ObservableSortedList<WideBannerModel>(1000, new BannerBaseComparer());
            this.FanArtBanners = new ObservableSortedList<FanArtBannerModel>(1000, new BannerBaseComparer());
            this.PosterBanners = new ObservableSortedList<PosterBannerModel>(1000, new BannerBaseComparer());
            this.Seasons = new ObservableSortedList<SeasonModel>(1000, new SeasonComparer());
            this.Actors = new ObservableCollection<ActorModel>();
            this.Watched = new ObservableSortedList<EpisodeModel>(1000, new EpisodeComparer(true));
            this.Unwatched = new ObservableSortedList<EpisodeModel>(1000, new EpisodeComparer(true));
            this.UnAired = new ObservableSortedList<EpisodeModel>(1000, new EpisodeComparer(true));
            this.Watched.CollectionChanged += UnAired_CollectionChanged;
            this.Unwatched.CollectionChanged += UnAired_CollectionChanged;
            this.UnAired.CollectionChanged += UnAired_CollectionChanged;
        }

        #endregion

        private void UnAired_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                if (!this.Unwatched.Any())
                {
                    var state = this.UnAired.Any() ? WatchState.UnAired : WatchState.Watched;
                    base.SetProperty(ref this.state, state, "State");
                }
                else
                {
                    base.SetProperty(ref this.state, WatchState.Unwatched, "State");
                }
            }
        }

        public EpisodeModel GetEpisode(int episodeID)
        {
            foreach (var season in this.Seasons)
            {
                foreach (var episode in season.Episodes)
                {
                    if (episode.ID == episodeID)
                    {
                        return episode;
                    }
                }
            }

            return null;
        }
    }
}
